#pragma once
#include "geometry.h"
#include "glm.h"
#include "glProgram.h"

struct FragmentInfo
{
    Vec3f position;
    Vec3f bc_coord;
    TGAColor color;
};

class IShader
{
    friend class glProgram;
public:
    void SetProgram(glProgram* program)
    {
        m_program = program;
    }
protected:
    template<typename T>
    T GetUniformVar(const std::string& name)
    {
        return m_program->GetUniformVar<T>(name);
    }

    glProgram* m_program;
};

class VertexShader : public IShader
{
    friend class glProgram;
public:
    virtual Vec4f vertex(std::shared_ptr<VertexInfoBase> vertexInfo) = 0;// 顶点着色器逻辑
    virtual void ConfirmVaryingVar() = 0;// 定义varying变量
protected:
    template<typename T>
    void AddVaryingVar(const std::string& name)
    {
        m_program->AddVaryingVar<T>(name);
    }
    template<typename T>
    void SetVaryingVar(const std::string& name, size_t index, const T& value)
    {
        m_program->SetVaryingVar<T>(name, value, index);
    }
};

class FragmentShader : public IShader
{
    friend class glProgram;
public:
    virtual bool fragment(FragmentInfo& info) = 0;
protected:
    template<typename T>
    T GetInterVaryingVar(const std::string& name, const Vec3f& barycentric_coords)
    {
        std::array<T, 3> varyings;

        T res{};
        for (int i = 0; i < 3; i++)
        {
            varyings[i] = m_program->GetVaryingVar<T>(name, i);
            res = res + varyings[i] * barycentric_coords[i];
        }
        res = res;
        return res;
    }
    template<>
    glm::mat4 GetInterVaryingVar(const std::string& name, const Vec3f& barycentric_coords)
    {
        std::array<glm::mat4, 3> varyings;
        varyings[0] = m_program->GetVaryingVar<glm::mat4>(name, 0);
        varyings[1] = m_program->GetVaryingVar<glm::mat4>(name, 1);
        varyings[2] = m_program->GetVaryingVar<glm::mat4>(name, 2);
        glm::mat4 res;
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                for (int k = 0; k < 3; k++)
                {
                    res[i][j] = res[i][j] + varyings[k][i][j] * barycentric_coords[k];
                    res[i][j] = res[i][j];
                }
            }
        }
        return res;
    }
	template<>
	glm::mat3 GetInterVaryingVar(const std::string& name, const Vec3f& barycentric_coords)
	{
		std::array<glm::mat3, 3> varyings;
		varyings[0] = m_program->GetVaryingVar<glm::mat3>(name, 0);
		varyings[1] = m_program->GetVaryingVar<glm::mat3>(name, 1);
		varyings[2] = m_program->GetVaryingVar<glm::mat3>(name, 2);
		glm::mat3 res;
		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				for (int k = 0; k < 3; k++)
				{
					res[i][j] = res[i][j] + varyings[k][i][j] * barycentric_coords[k];
                    res[i][j] = res[i][j];
				}
			}
		}
        return res;
	}

private:
    std::array<std::shared_ptr<VertexInfoBase>, 3> m_vertexInfos;
};
